package myicesscheduler.web;

import java.util.Calendar;
import java.util.Date;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import myicesscheduler.hibernate.LectureCoordinatorTransaction;
import myicesscheduler.hibernate.LectureNoticeTransaction;
import myicesscheduler.hibernate.LectureSponsorTransaction;
import myicesscheduler.hibernate.LectureTransaction;
import myicesscheduler.hibernate.ReservationTypeTransaction;
import myicesscheduler.hibernate.SpeakerTransaction;
import myicesscheduler.hibernate.TimeSlotTransaction;
import myicesscheduler.hibernate.autogenerated.Lecture;
import myicesscheduler.hibernate.autogenerated.LectureCoordinator;
import myicesscheduler.hibernate.autogenerated.LectureNotice;
import myicesscheduler.hibernate.autogenerated.LectureSponsor;
import myicesscheduler.hibernate.autogenerated.Speaker;
import myicesscheduler.hibernate.autogenerated.TimeSlot;
import myicesscheduler.web.LectureCommand.TimeSlotCommand;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.web.bind.ServletRequestDataBinder;
import org.springframework.web.multipart.support.ByteArrayMultipartFileEditor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.SimpleFormController;
import org.springframework.web.servlet.view.RedirectView;

public class AdminLectureController extends SimpleFormController
{
   private static final int defaultTimeIntervalInMinuate = 30; // 15, 20 and 60 are also possible.
   protected final Log logger = LogFactory.getLog( getClass() );
   private static final String URL = "adminLectureAdd.spring";
   public final static String param_lectureId = "lectureId";
   private final static String attr_targetLecture = "targetLecture";

   private final LectureCommand.Month[] months = { LectureCommand.Month.Jan,
         LectureCommand.Month.Feb, LectureCommand.Month.Mar, LectureCommand.Month.Apr,
         LectureCommand.Month.May, LectureCommand.Month.Jun, LectureCommand.Month.Jul,
         LectureCommand.Month.Aug, LectureCommand.Month.Sep, LectureCommand.Month.Oct,
         LectureCommand.Month.Nov, LectureCommand.Month.Dec };

   public ModelAndView handleRequest( HttpServletRequest request, HttpServletResponse response )
         throws Exception
   {
      if( false == LoginController.isAdmin( request ) ) return LectureListController.redirect();

      Integer lectureId = null;
      String lectureIdString = request.getParameter( param_lectureId );
      if( null != lectureIdString ) lectureId = Integer.valueOf( lectureIdString );
      final boolean bAddCmd = ( null == lectureId );

      final ModelAndView mv = super.handleRequest( request, response );
      final Lecture targetLecture = (Lecture) mv.getModel().get( attr_targetLecture );
      if( null != targetLecture )
         return LectureListController.redirect( targetLecture.getInternalId() );

      mv.setViewName( bAddCmd ? "adminLectureAdd" : "adminLectureModify" );
      mv.addObject( "today", new Date() );
      mv.addObject( "months", months );
      mv.addObject( "reservationTypes", ReservationTypeTransaction.getAllReservationTypes() );
      mv.addObject( "lectureIdForModify", lectureId );
      return mv;
   }

   @Override
   protected ModelAndView onSubmit( Object command ) throws Exception
   {
      if( null == command ) logger.info( "onSubmit: command object is null" );
      final LectureCommand cmd = (LectureCommand) command;
      logger.info( "onSubmit: " + cmd.toString() );

      final boolean bForAddingNewLecture = cmd.isForAddingNewLecture();
      if( bForAddingNewLecture )
         return onSubmit_AddNewLecture( command );
      else
         return onSubmit_ModifyLecture( command );
   }

   public ModelAndView onSubmit_AddNewLecture( Object command ) throws Exception
   {
      final LectureCommand cmd = (LectureCommand) command;

      final LectureNotice lectureNotice = LectureNoticeTransaction.insertOrGetIfAlreadyExist( cmd
            .getNotice() );

      final LectureCoordinator lectureCoordinator = LectureCoordinatorTransaction
            .insertOrGetIfAlreadyExist( cmd.getCoordinatorName(), cmd.getCoordinatorEmail(), cmd
                  .getCoordinatorPhone() );

      final LectureSponsor lectureSponsor = LectureSponsorTransaction
            .insertOrGetIfAlreadyExist( cmd.getSponsorName() );

      final Speaker speaker = SpeakerTransaction.insertOrGetIfAlreadyExist( cmd.getSpeakerName(),
            cmd.getSpeakerOrganization(), cmd.getSpeakerHomepage() );

      cmd.mergeConsequtiveTimeSlots();

      final boolean bAlreadyLectureExist = ( 0 != LectureTransaction.getLecture_byTopicAndSpeaker(
            cmd.getTopic(), speaker ).size() );
      if( bAlreadyLectureExist ) throw new RuntimeException( "Lecture already exist." );

      final String paperFilename = MyFileUtil.storeFile( cmd.getPaperFile(), MyFolderUtil.uploadFolder );

      final Lecture targetLecture = LectureTransaction.insert( cmd.getTopic(), cmd
            .getTopicAbstract(), cmd.getPaperUrl(), paperFilename, speaker, lectureCoordinator,
            lectureNotice, lectureSponsor );

      insertTimeSlots( targetLecture.getInternalId(), cmd.getTimeSlotCommands() );
      logger.info( "onSubmit: a new lecture is added." );

      final ModelAndView mv = new ModelAndView( this.getSuccessView() );
      mv.addObject( attr_targetLecture, LectureTransaction.getLecture( targetLecture
            .getInternalId() ) );
      return mv;
   }

   public ModelAndView onSubmit_ModifyLecture( Object command ) throws Exception
   {
      final LectureCommand cmd = (LectureCommand) command;

      final LectureNotice lectureNotice = LectureNoticeTransaction.insertOrGetIfAlreadyExist( cmd
            .getNotice() );

      final LectureCoordinator lectureCoordinator = LectureCoordinatorTransaction
            .insertOrGetIfAlreadyExist( cmd.getCoordinatorName(), cmd.getCoordinatorEmail(), cmd
                  .getCoordinatorPhone() );

      final LectureSponsor lectureSponsor = LectureSponsorTransaction
            .insertOrGetIfAlreadyExist( cmd.getSponsorName() );

      final Speaker speaker = SpeakerTransaction.insertOrGetIfAlreadyExist( cmd.getSpeakerName(),
            cmd.getSpeakerOrganization(), cmd.getSpeakerHomepage() );

      cmd.mergeConsequtiveTimeSlots();

      final int lectureId = cmd.getLectureIdToModify();
      final Lecture targetLecture = LectureTransaction.getLecture( lectureId );
      targetLecture.setTopic( cmd.getTopic() );
      targetLecture.setTopicAbstract( cmd.getTopicAbstract() );
      targetLecture.setPaperUrl( cmd.getPaperUrl() );
      targetLecture.setSpeaker( speaker );
      targetLecture.setLectureCoordinator( lectureCoordinator );
      targetLecture.setLectureNotice( lectureNotice );
      targetLecture.setLectureSponsor( lectureSponsor );
      LectureTransaction.update( targetLecture );

      TimeSlotTransaction.remove_byLecture( targetLecture );
      insertTimeSlots( targetLecture.getInternalId(), cmd.getTimeSlotCommands() );
      logger.info( "onSubmit: a lecture is modified." );

      final ModelAndView mv = new ModelAndView( this.getSuccessView() );
      mv.addObject( attr_targetLecture, targetLecture );
      return mv;
   }

   private void insertTimeSlots( int lectureId, TimeSlotCommand[] timeSlots ) throws Exception
   {
      for( TimeSlotCommand timeSlot : timeSlots )
      {
         if( false == timeSlot.isEnabled() ) continue;

         final Date beginDate = timeSlot.getBeginDate();
         final Date endDate = timeSlot.getEndDate();
         final String location = timeSlot.getLocation();
         final String note = timeSlot.getNote();

         TimeSlotTransaction.insert( beginDate, endDate, note, timeSlot.getReservationType(),
               location, lectureId, null, null );
      }
   }

   @Override
   protected Object formBackingObject( HttpServletRequest request ) throws Exception
   {
      final boolean bAddingCommand = ( null == request.getParameter( param_lectureId ) );

      if( bAddingCommand ) return formBackingObject_forAddLecture( request );

      return formBackingObject_forModifyLecture( request );
   }

   private Object formBackingObject_forAddLecture( HttpServletRequest request ) throws Exception
   {
      final LectureCommand cmd = new LectureCommand();
      cmd.setForAddingNewLecture( true );
      cmd.setTimeSlotIntervalInMinute( defaultTimeIntervalInMinuate );
      logger.info( "formBackingObject: Adding a new lecture: " + cmd.toString() );
      return cmd;
   }

   Object formBackingObject_forModifyLecture( HttpServletRequest request ) throws Exception
   {
      final LectureCommand cmd = new LectureCommand();
      cmd.setForAddingNewLecture( false );

      Integer lectureId;
      try
      {
         lectureId = Integer.valueOf( request.getParameter( param_lectureId ) );
      }
      catch( Exception e )
      {
         return LectureListController.redirect();
      }
      cmd.setLectureIdToModify( lectureId );

      final Lecture lecture = LectureTransaction.getLecture( lectureId );
      if( null == lecture ) return LectureListController.redirect();

      final LectureCoordinator lectureCoordinator = lecture.getLectureCoordinator();
      cmd.setCoordinatorName( lectureCoordinator.getName() );
      cmd.setCoordinatorEmail( lectureCoordinator.getEmail() );
      cmd.setCoordinatorPhone( lectureCoordinator.getPhone() );

      final LectureNotice lectureNotice = lecture.getLectureNotice();
      if( null != lectureNotice ) cmd.setNotice( lectureNotice.getNoticeMessage() );

      final LectureSponsor lectureSponsor = lecture.getLectureSponsor();
      if( null != lectureSponsor ) cmd.setSponsorName( lectureSponsor.getName() );

      final Speaker speaker = lecture.getSpeaker();
      if( null != speaker )
      {
         cmd.setSpeakerName( speaker.getName() );
         cmd.setSpeakerOrganization( speaker.getOrganization() );
         cmd.setSpeakerHomepage( speaker.getHomepage() );
      }

      final List< TimeSlot > lectureTimeSlots = TimeSlotTransaction.getLectureTimeSlots( lecture );
      if( 0 != lectureTimeSlots.size() )
      {
         final Calendar cal = Calendar.getInstance();
         cal.setTime( lectureTimeSlots.get( 0 ).getBeginTime() );
         cmd.setDateYear( cal.get( Calendar.YEAR ) );
         cmd.setDateMonth( LectureCommand.Month.getMonth( cal.get( Calendar.MONTH ) ) );
         cmd.setDateDay( cal.get( Calendar.DAY_OF_MONTH ) );
      }

      cmd.setTopic( lecture.getTopic() );
      cmd.setTopicAbstract( lecture.getTopicAbstract() );
      cmd.setPaperUrl( lecture.getPaperUrl() );
      cmd.setTimeSlotCommands( TimeSlotTransaction.getTimeSlotList( lecture ),
            defaultTimeIntervalInMinuate );

      logger.info( "formBackingObject: Modify a lecture: " + cmd.toString() );
      return cmd;
   }

   public static ModelAndView redirect()
   {
      return new ModelAndView( new RedirectView( URL ) );
   }

   protected void initBinder( HttpServletRequest request, ServletRequestDataBinder binder )
         throws ServletException
   {
      // to actually be able to convert Multipart instance to byte[]
      // we have to register a custom editor
      binder.registerCustomEditor( byte[].class, new ByteArrayMultipartFileEditor() );
      // now Spring knows how to handle multipart object and convert them
   }
}
